package thread;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: wx
 * Date: 2024-01-13
 * Time: 18:53
 */

import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;

import java.util.Comparator;
import java.util.PriorityQueue;

/**  自己实现一个简单的Timer
 *   timer 里边需要包含哪些内容
 *   1.需要一个线程,负责掐时间,等任务到了合适的时间,这个线程负责执行
 *   2.还需要一个队列/数组,能够保存所有的 schedule 进来的任务
 *   直观想,这个线程,就可以不断的去扫描上诉队列的每个元素,看每个任务是否时间到了,到时间就执行
 *   如果队列很长,这个过程开销会很大
 *   每个任务都是带有 delay 时间的. 肯定是先执行时间小的,然后执行时间大的
 *   我们使用优先级队列
 *   扫描线程就不必便利了,只需要关注队首元素是否到时间,如果时间没到,后续其他元素一定也没到时间
 *   就可以使用标准库的priorityQueue(线程不安全) 手动加锁
 *   标准库也提供了PriorityBlockingQueue(线程安全)  再从此处的场景中,不太好控制,容易出问题
 *
 */

/*

//通过这个类,来描述一个任务
class MytimerTask implements Comparable<MytimerTask>{
    //需要一个时间来执行这个任务
    //此处约定这个 time 是一个 ms 级别的时间戳
    //                             时间戳:以 1970 年 1 月 1 日 0 时 0 分 0 秒,为基准,计算当前时刻和基准时刻的 秒数/毫秒数/微秒数...之差
    private long time;
    //实际要执行的代码
    private Runnable runnable;


    public MytimerTask(Runnable runnable, long delay){
        this.runnable = runnable;
        //计算一下真正要执行任务的绝对时间.(使用绝对是时间,方便判定任务是否到达时间)
        this.time = System.currentTimeMillis() + delay;
    }
    public long GetTime() {
        return time;
    }
    public void run() {
        runnable.run();
    }
    //是否还有其他问题??
    */
/*
    他要放到优先级队列里边的,有些集合类,是对于元素有特定要求的
    PriorityQueue,TreeMap,Treeset... 都是要求元素是'可比较大小的'
    Comparable,Comparator
    HashMap,HashSet,则是要求元素是"可比较相等的" "可hash的"
                                equals       hashCode
                                本身Object 提供了这两个方法的实现
                                有些时候,为了让hash更高效,可能需要重写equals和 hashCode
    *//*


    //期望跟去时间来确定
    @Override
    public int compareTo(MytimerTask o) {
        //return (int) (o.time - this.time);
        return (int) (this.time - o.time);
    }
}

//通过这个类,来实现一个定时器
class Mytimer{
    // 负责扫描任务队列,执行任务的线程.
    private Thread t = null;
    //任务队列
    private PriorityQueue<MytimerTask> queue = new PriorityQueue();
    public void schedule(Runnable runnable,long delay){
        MytimerTask task = new MytimerTask(runnable,delay);
        queue.offer(task);
    }

    // 构造方法 创建扫描线程,让扫描线程来完成判定和执行
    public  Mytimer() {
        t = new Thread(() ->{
           //扫描线程就需要循环的反复的扫描队首元素,然后判定对手元素是不是时间到了.
           //如果时间没到,啥都不干
           //如果时间到了,就执行这个任务并且把这个任务从队列中删除出去
            while(true){
                if(queue.isEmpty()){
                    //暂时先不处理
                    continue;
                }
                MytimerTask task =queue.peek();
                //获取到当前时间
                long curTime = System.currentTimeMillis();
                if(curTime >= task.GetTime()){
                    //当前时间已经达到了任务时间,就可以执行任务了
                    queue.poll();
                    task.run();
                }else{
                    //当前时间还没到,先不执行
                    continue;
                }
            }
        });
    }
    //此时还是存在两个核心问题?
    1.线程安全问题
    例如下述代码,一个在主线程里边添加元素,一个在另一个线程里删除元素;两个不同的线程操作的是同一个任务队列
    多个线程操作同一个队列,就存在线程安全问题,在此处我们进行加锁在解决;
    2.
}
*/
//通过这个类,来描述一个任务
class MytimerTask implements Comparable<MytimerTask>{
    //需要一个时间来执行这个任务
    private long time;
    //实际要执行的代码
    private Runnable runnable;


    public MytimerTask(Runnable runnable, long delay){
        this.runnable = runnable;
        //计算一下真正要执行任务的绝对时间.(使用绝对是时间,方便判定任务是否到达时间)
        this.time = System.currentTimeMillis() + delay;
    }

    public long getTime() {
        return time;
    }

    public void run() {
        runnable.run();
    }
    //期望跟去时间来确定


    @Override
    public int compareTo(MytimerTask task) {
        return (int) (this.time - task.time);
    }
}

//通过这个类,来实现一个定时器
class Mytimer {
    // 负责扫描任务队列,执行任务的线程.
    private Thread t = null;
    //任务队列
    private PriorityQueue<MytimerTask> queue = new PriorityQueue();
    private Object locker = new Object();

    public void schedule(Runnable runnable, long delay) {
        synchronized(locker){
        MytimerTask task = new MytimerTask(runnable, delay);
            queue.offer(task);
            //添加新的元素之后,就可以唤醒扫描线程的wait了
            locker.notify();
        }
    }

    public void cancle(){
        //结束线程,通过interrupt方法
    }

    // 构造方法 创建扫描线程,让扫描线程来完成判定和执行
  public Mytimer() {
        t = new Thread(() -> {
            //扫描线程就需要循环的反复的扫描队首元素,然后判定对手元素是不是时间到了.
            //如果时间没到,啥都不干
            //如果时间到了,就执行这个任务并且把这个任务从队列中删除出去
            while (true) {
                try {
                    synchronized (locker){
                        while (queue.isEmpty()) {
                            //暂时先不处理
                            locker.wait();
                        }
                        MytimerTask task = queue.peek();
                            //获取到当前时间
                        long curTime = System.currentTimeMillis();
                        if (curTime >= task.getTime()) {
                                 //当前时间已经达到了任务时间,就可以执行任务了
                                 queue.poll();
                                 task.run();
                        } else {
                            //当前时间还没到,先不执行

                          locker.wait(task.getTime() - curTime);
                        }
                    }
                } catch (InterruptedException e) {
                        e.printStackTrace();
                }
            }
        });
        t.start();
    }
}


public class ThreadDemo30 {
    public static void main(String[] args) {
        Mytimer mytimer = new Mytimer();
        mytimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 3000");
            }
        }, 3000);

        mytimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 2000");
            }
        }, 2000);

        mytimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 1000");
            }
        }, 1000);
        System.out.println("hello main");
    }
}

